#include "dnsimpl.h"
#include "protocol/dns.h"

CDnsImpl::CDnsImpl()
{
}
CDnsImpl::~CDnsImpl(void)
{
    logi("CDnsImpl::~CDnsImpl");
}
HRESULT CDnsImpl::init_class(const _lComPtr<IAsynFrame>& AsynFrame)
{
    HRESULT hr = S_OK;

    hr = AsynFrame->CreateIo(io_udp_socket, (IBase**)&m_pISocket);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}
HRESULT CDnsImpl::Post(LPCSTR hostname, IBase* pProc)
{
    HRESULT hr = S_OK;
    logi("SendUrl ==>%s", hostname);
    dns_t query;
    memset(&query, 0, sizeof(query));
    query.hdr.transaction_id = getpid();
    query.hdr.qr = DNS_QUERY;
    query.hdr.rd = 1;
    query.hdr.nquestion = 1;

    dns_rr_t question;
    memset(&question, 0, sizeof(question));
    s_strcpy(question.name, sizeof(question.name), hostname);
    question.rtype = DNS_TYPE_A;
    question.rclass = DNS_CLASS_IN;

    query.questions = &question;

    char buf[1024] = {0x00};
    int buflen = sizeof(buf);
    buflen = dns_pack(&query, buf, buflen);

    if (buflen < 0) {
        return buflen;
    }

    _lComPtr<IBase> pIBase;
    hr = this->QueryInterface(IID_IBase, (void**)&pIBase);
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pIOperation->BindEvent(pIBase);
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pIOperation->Attach((buf_ptr)buf, 0, buflen);
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pISocket->CreateSock();
    rc_error(hr == S_OK, E_FAIL);

    buf_len len = 0;
    // len = m_pISocket->SyncSend("114.114.114.114", 53, (buf_ptr)buf, buflen);
    // rc_error(len != 0, E_FAIL);

    hr = m_pIOperation->ClearPtr();
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pIOperation->GetLen(&len);
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pIOperation->SetIoParam(0, len);
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pISocket->ReadIo("114.114.114.114", 53, m_pIOperation, 0);
    rc_error(hr == S_OK, E_FAIL);

    m_sem.Wait();

    return S_OK;
}
HRESULT CDnsImpl::Close()
{
    HRESULT hr = S_OK;

    hr = m_pISocket->CloseSock();
    rc_error(hr == S_OK, E_FAIL);

    hr = m_pIOperation->UnBindEvent();
    rc_error(hr == S_OK, E_FAIL);

    m_pISocket.dispose();

    m_pIOperation.dispose();

    return S_OK;
}

HRESULT CDnsImpl::OnComplete(IBase* pOperation, crtid cid, crterr err)
{
    char ip[16] = {0x00};
    int ret = 0;
    uint32_t addrs[32] = {0x00};

    dns_t resp;
    memset(&resp, 0, sizeof(resp));

    _lComPtr<IOperation> pOper;
    pOperation->QueryInterface(IID_IOperation, (void**)&pOper);

    crterr error = 0;
    buf_len size = 0;
    pOper->GetResult(&error, &size);

    buf_ptr ptr;

    pOper->GetPtr(&ptr);

    int nparse = dns_unpack((char*)ptr, size, &resp);
    if (nparse != size) { }

    dns_rr_t* rr = resp.answers;
    int addr_cnt = 0;
    if (/*resp.hdr.transaction_id != query.hdr.transaction_id ||*/ resp.hdr.qr != DNS_RESPONSE || resp.hdr.rcode != 0) {
        ret = S_ERROR;
        return 0;
    }

    if (resp.hdr.nanswer == 0) {
        ret = 0;
        return 0;
    }

    for (int i = 0; i < resp.hdr.nanswer; ++i, ++rr) {
        char name[256];

        if (rr->rtype == DNS_TYPE_A) {
            if (addr_cnt < 16 && rr->datalen == 4) {
                memcpy(addrs + addr_cnt, rr->data, 4);
            }
            ++addr_cnt;
        } else if (rr->rtype == DNS_TYPE_CNAME) {
            dns_name_decode(rr->data, name);
        }
    }
    ret = addr_cnt;

    for (int i = 0; i < ret; ++i) {
        _inet_ntop(AF_INET, (void*)&addrs[i], ip, 16);
        logi("ip==>[%s]", ip);
    }
    dns_free(&resp);

    m_sem.Post();
    return S_OK;
}
